let utils = require('./utils');

let keyIndex = 0; //全局的元素index
function diff(oldTree, newTree) {
    //记录差异的空对象。key就是老节点在原来虚拟DOM树中的序号，值就是一个差异对象数组
    let patches = {};
    let index = 0;
    // 递归树：判断节点的各种修改情况，将对应比较后的结果统一放到补丁包
    walk(oldTree, newTree, index, patches);
    return patches;
}

//判断节点的各种修改情况：
// REMOVE: 'REMOVE',     //此节点被移除(新DOM节点不存在)
// ATTRS: "ATTRS",       //属性被改变
// TEXT: "TEXT",         //文本内容被改变
// REPLACE: "REPLACE",   //节点要被整个替换  
function walk(oldNode, newNode, index, patches) {
    let currentPatches = [];  //这个数组里记录了所有的oldNode的变化
    //1、如果新节点没有了，则认为此节点被删除了 
    // { type: REMOVE, index } 
    if (!newNode) {
        currentPatches.push({ type: utils.REMOVE, index });
    } else if (utils.isString(oldNode) && utils.isString(newNode)) {
    //3、如果说，新老节点都是文本节点的话
        //如果新的字符符值和旧的不一样
        if (oldNode != newNode) {  //文本改变 
            currentPatches.push({ type: utils.TEXT, content: newNode });
        }
    } else if (oldNode.tagName == newNode.tagName) {
        //2、节点名相同，比较新旧元素的属性对象  
        // { type:'ATTRS', attrs:{class:TagName} }
        let attrsPatch = diffAttr(oldNode.attrs, newNode.attrs);
        //如果新旧元素有差异 的属性的话
        if (Object.keys(attrsPatch).length > 0) {
            //添加到差异数组中去
            currentPatches.push({ type: utils.ATTRS, attrs: attrsPatch });
        }
        //自己比完后再比自己的儿子们
        diffChildren(oldNode.children, newNode.children, index, patches, currentPatches);
    } else {
        //3、节点类型不相同，直接采用替换模式
        currentPatches.push({ type: utils.REPLACE, node: newNode });
    }

    // 当前元素确实有补丁，将元素对应的补丁，填充到补丁包中。
    if (currentPatches.length > 0) {
      patches[index] = currentPatches;
    }
}

//老的节点的儿子们 新节点的儿子们 父节点的序号 完整补丁对象 当前旧节点的补丁对象
function diffChildren(oldChildren, newChildren, index, patches, currentPatches) {
    oldChildren.forEach((child, idx) => {
        //index 是每次遍历子节点 ++index 从1开始递增
        // 而 keyIndex 代表的是DOM树种的元素的index，在DOM树种是确定不变的。
        walk(child, newChildren[idx], ++keyIndex, patches);
    });
}

//2、节点名相同，比较新旧元素的属性对象
function diffAttr(oldAttrs, newAttrs) {
    let attrsPatch = {};
    for (let attr in oldAttrs) {
        //如果说老的属性和新属性不一样。
        // 一种是值改变 ，一种是属性被删除了，有可能是undefined 
        if (oldAttrs[attr] != newAttrs[attr]) {
            attrsPatch[attr] = newAttrs[attr]; 
        }
    }
    //新节点的新增属性
    for (let attr in newAttrs) {
        if (!oldAttrs.hasOwnProperty(attr)) {
            attrsPatch[attr] = newAttrs[attr];
        }
    }
    return attrsPatch;
}

module.exports = diff; 